<?php
/**
 * Created by PhpStorm.
 * User: 戎大富
 * Date: 2018/6/13
 * Time: 10:40
 */
namespace app\api\controller;
use think\Controller;
use app\api\model\User as UserModel;
use think\Db;

class  Weixin extends Controller {
    // 定义配置项
    private $config=array(
        'APPID'              => 'wx5518b83be52f8d41', // 微信支付APPID.
        'MCHID'              => '1513192391', // 微信支付MCHID 商户收款账号
        'KEY'                => '3GU4e3oiUVezl63h8lgBYNvEPYSr7M7A', // 微信支付KEY
        'APPSECRET'          => 'b802200fb0c5ddcb0ea290acf09dd9fc', // 公众帐号secert (公众号支付专用)
        'NOTIFY_URL'         => 'http://south.xuanwumobile.com/api/Weixin/huidiao', // 接收支付状态的连接

    );
    public $body;//描述
    public $total_fee;//金额
    public $out_trade_no;//订单号

    // 构造函数
    public function __construct($config=null){
        // 如果配置项为空 则直接返回
        if (!empty($config)) {
            $this->config=$config;
        }
    }
    
    /**
     * 微信支付接口
     * @return string
     * classroom_id：课程ID,当type为1时，传此参数
     * user_id：用户ID,
     * type：1 课程购买  2 捐赠  3 打赏
     * price：打赏或捐赠的价格，当type为2或者3时传
     * activity_id：捐赠的活动ID，当type为2时，传此值
     */
    public function WeChatApi(){
        
        $params = input('param.');
//        $auth = $this->checkAll($user_id,$time,$value,$reqKey);
//        if($auth == false){
//            $this->apiError(301);
//        }
        if($params['type'] == 1){//课程订单
            $classroomInfo = Db::table('up_classroom')->where('id', $params['classroom_id'])
                ->find();
            $data = array();
            $data['order_number'] = time().rand(1000000,9999999);//订单号
            $data['user_id'] = $params['user_id'];//用户
            $data['order_time'] = date('Y-m-d H:i:s');//订单创建时间
            $data['order_type'] = 2;//付款方式  1支付宝  2微信
            $data['order_money'] = $classroomInfo['classroom_price'];//订单价格
            $data['classroom_id'] = $params['classroom_id'];//课程ID
            $data['order_status'] = 2;//订单状态
            $this->body = '南丁格尔课程购买';
            $this->total_fee = $data['order_money'] * 100;
            $this->out_trade_no = $data['order_number'];
            $res = Db::table('up_classroom_order')->insert($data);
        }elseif($params['type'] == 2){//捐赠
            $data = array();
            $data['donate_number'] = time().rand(1000000,9999999);//订单号
            $data['user_id'] = $params['user_id'];//用户
            $data['donate_time'] = date('Y-m-d H:i:s');//订单创建时间
            $data['donate_type'] = 2;//付款方式  1支付宝  2微信
            $data['donate_money'] = $params['price'];//订单价格
            $data['activity_id'] = $params['activity_id'];//捐赠的活动ID
            $data['donate_status'] = 2;//订单状态
            $data['cate'] = 1;//记录的分类  1 捐赠   2 打赏
            $this->body = '南丁格尔活动捐赠';
            $this->total_fee = $data['donate_money'] * 100;
            $this->out_trade_no = $data['donate_number'];
            $res = Db::table('up_activity_order')->insert($data);
        }else{//打赏
            $data = array();
            $data['donate_number'] = time().rand(1000000,9999999);//订单号
            $data['user_id'] = $params['user_id'];//用户
            $data['donate_time'] = date('Y-m-d H:i:s');//订单创建时间
            $data['donate_type'] = 2;//付款方式  1支付宝  2微信
            $data['donate_money'] = $params['price'];//订单价格
            $data['donate_status'] = 2;//订单状态
            $data['cate'] = 2;//记录的分类  1 捐赠   2 打赏
            $this->body = '南丁格尔打赏';
            $this->total_fee = $data['donate_money'] * 100;
            $this->out_trade_no = $data['donate_number'];
            $res = Db::table('up_activity_order')->insert($data);
        }
        if($res){
            $order = array();
            $order['body'] = $this->body;//描述
            $order['total_fee'] = $this->total_fee;//订单金额
            $order['out_trade_no'] = $this->out_trade_no;//订单号
            $order['product_id'] = '';//产品ID
            $order['trade_type'] = 'APP';//类型
            $respont = $this->WeChatPayReady($order);
            return json_encode(['status'=>200,'msgsss'=>$respont]);
        }else{
            return json_encode(['status'=>0,'msgsss'=>'生成订单失败，请联系管理员']);
        }
    }
    
    /**
     * 统一下单
     * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型：JSAPI，NATIVE，APP)
     */
    public function WeChatPayReady($order,$type=null){
        // 获取配置项
        $weixinpay_config=$this->config;
        $config=array(
            'appid'=>$weixinpay_config['APPID'],
            'mch_id'=>$weixinpay_config['MCHID'],
            'nonce_str'=>'test',
            'spbill_create_ip'=>$_SERVER['REMOTE_ADDR'],
            'notify_url'=>$weixinpay_config['NOTIFY_URL']
        );
        if(!empty($type)){
            $config['notify_url']='http://south.xuanwumobile.com/api/Weixin/huidiao';
        }
        // 合并配置数据和订单数据
        $data=array_merge($order,$config);

        // 生成签名
        $sign=$this->makeSign($data);
        $data['sign']=$sign;
        $xml=$this->toXml($data);
//        <xml>
//            <body><![CDATA[南丁格尔课程购买]]></body>
//            <total_fee>0</total_fee>
//            <out_trade_no>15359632423617767</out_trade_no>
//            <product_id><![CDATA[]]></product_id>
//            <trade_type><![CDATA[APP]]></trade_type>
//            <appid><![CDATA[wx5518b83be52f8d41]]></appid>
//            <mch_id>1513192391</mch_id>
//            <nonce_str><![CDATA[test]]></nonce_str>
//            <spbill_create_ip><![CDATA[127.0.0.1]]></spbill_create_ip>
//            <notify_url><![CDATA[http://south.xuanwumobile.com/api/Weixin/huidiao]]></notify_url>
//            <sign><![CDATA[7203312FA0E1F814BEA0674495BBF9CA]]></sign>
//        </xml>
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//接收xml数据的文件
        $header[] = "Content-type: text/xml";//定义content-type为xml,注意是数组
        $ch = curl_init ($url);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $response = curl_exec($ch);
        if(curl_errno($ch)){
            print curl_error($ch);
        }
        curl_close($ch);
        $result=$this->toArray($response);
        $result['sign']=$sign;
        $result['nonce_str']='test';
        $result2 = $this->getOrder($result['prepay_id']);
        return $result2;///
    }
    
    /**
     * 获取时间
     * @return array
     */
    public function getDateBytimeDate($string){
        $replacement = '-';
        $replacement2 = ':';
        $a = substr_replace($string, $replacement, 4, 0);
        $b = substr_replace($a, $replacement, 7, 0);
        $c = substr_replace($b, ' ', 10, 0);
        $d = substr_replace($c, $replacement2, 13, 0);
        $e = substr_replace($d, $replacement2, 16, 0);
        return $e;
    }
    
    /**
     * 验证
     * @return array 返回数组格式的notify数据
     */
    public function huidiao(){
        // 获取xml
        $xml=file_get_contents('php://input', 'r');
//         $this->log($xml);
        // 转成php数组
        $getInfo=$this->toArray($xml);
//         $s = var_export($data);
//        $this->log($s);
        // 保存原sign
        $data_sign=$getInfo['sign'];
        // sign不参与签名
        unset($getInfo['sign']);
        $sign=$this->makeSign($getInfo);
        // 判断签名是否正确  判断支付状态
        if ($sign===$data_sign && $getInfo['return_code']=='SUCCESS' && $getInfo['result_code']=='SUCCESS') {
            $num = $getInfo['out_trade_no'];
            $dataOrder = array();
            $dataOrder['pay_time'] = $this->getDateBytimeDate("{$getInfo['time_end']}");//付款时间
            Db::startTrans();
            try{
                //首先根据订单号到课程订单中查找，如果此订单号不在课程订单中，这说明是打赏或捐赠
                $info = Db::table('up_classroom_order')->where('order_number',$num)->find();
                if($info){//在课程订单中
                    $dataOrder['order_status'] = 1;//订单状态  1已完成   2未支付
                    //支付成功，更新课程订单表信息
                    Db::table('up_classroom_order')->where('order_number',$num)->update($dataOrder);
                    //插入一条数据到我的购买课程中
                    $info = Db::table('up_classroom_order')->where('order_number',$num)->find();
                    $data = [
                        'classroom_id' => $info['classroom_id'],//课程ID
                        'user_id' => $info['user_id'],//用户ID
                        'create_time' => $dataOrder['pay_time']
                    ];
                    Db::table('up_classroom_user')->insert($data);
                }else{
                    $dataOrder['donate_status'] = 1;//订单状态  1已完成   2未支付
                    //支付成功，更新课程订单表信息
                    Db::table('up_activity_order')->where('donate_number',$num)->update($dataOrder);
                }
                Db::commit();
                $result=$getInfo;
            }catch (\Exception $e) {
                Db::rollback();
                $result=false;
            }
        }else{
            $result=false;
        }
        // 返回状态给微信服务器----------------
        if ($result) {
            $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }else{
            $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
        }
        echo $str;
        exit();
        return $result;
    }
    
    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public function makeSign($data){
        // 去空
        $data=array_filter($data);
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string_a=http_build_query($data);
        $string_a=urldecode($string_a);
        //签名步骤二：在string后加入KEY
        $config=$this->config;
        $string_sign_temp=$string_a."&key=".$config['KEY'];
        //签名步骤三：MD5加密
        $sign = md5($string_sign_temp);
        // 签名步骤四：所有字符转为大写
        $result=strtoupper($sign);
        return $result;
    }

    /**
     * 将xml转为array
     * @param  string $xml xml字符串
     * @return array       转换得到的数组
     */
    public function toArray($xml){
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $result;
    }
    
    /**
     * 输出xml字符
     * @throws WxPayException
     **/
    public function toXml($data){
        //var_dump($data);
        if(!is_array($data) || count($data) <= 0){
            //throw new WxPayException("数组数据异常！");
        }
        $xml = "<xml>";
        foreach ($data as $key=>$val){
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }

    /**
     * 获取jssdk需要用到的数据
     * @return array jssdk需要用到的数据
     */
    public function getParameters($out_trade_no,$total_fee,$type=null){
        $config = $this->config;

        // 如果没有get参数没有code；则重定向去获取openid；
        if (!isset($_GET['code'])) {
            // 获取订单号
            $out_trade_no=$out_trade_no;
            // 返回的url
            $redirect_uri=U('Home/Pay/weixin',array('order_num'=>$out_trade_no,'total_money'=>$total_fee),'',true);
            //如果为充值支付 跳转到 另一个URL
            if(!empty($type)){
                $redirect_uri = U('Home/Pay/rechargeWeixin',array('order_num'=>$out_trade_no,'total_money'=>$total_fee,'type'=>$type),'',true);
            }

            $redirect_uri=urlencode($redirect_uri);

            $url='https://open.weixin.qq.com/connect/oauth2/authorize?appid='.$config['APPID'].'&redirect_uri='.$redirect_uri.'&response_type=code&scope=snsapi_base&order_num='.$out_trade_no.'&total_money='.$total_fee.'#wechat_redirect';

            redirect($url);
        }else{

            // 如果有code参数；则表示获取到openid
            $code=I('get.code');

            // 取出订单号
            $out_trade_no=I('get.order_num');
            $total_fee=I('get.total_money');
            $type=I('get.type',0);


            // 组合获取prepay_id的url
            $url='https://api.weixin.qq.com/sns/oauth2/access_token?appid='.$config['APPID'].'&secret='.$config['APPSECRET'].'&code='.$code.'&grant_type=authorization_code';
            // curl获取prepay_id
            $result=$this->curl_get_contents($url);

            $result=json_decode(trim($result,chr(239).chr(187).chr(191)),true);
            $openid=$result['openid'];
            // 订单数据  请根据订单号out_trade_no 从数据库中查出实际的body、total_fee、out_trade_no、product_id
            $order=array(
                'body'=>'风云夺宝',// 商品描述（需要根据自己的业务修改）
                'total_fee'=>$total_fee*100,// 订单金额  以(分)为单位（需要根据自己的业务修改）
                'out_trade_no'=>$out_trade_no,// 订单号（需要根据自己的业务修改）
                'product_id'=>'1',// 商品id（需要根据自己的业务修改）
                'trade_type'=>'JSAPI',// JSAPI公众号支付
                'openid'=>$openid// 获取到的openid
            );
            //dump($order);exit();
            // 统一下单 获取prepay_id
            $unified_order=$this->unifiedOrder($order,$type);
            // 获取当前时间戳
            $time=time();
            // 组合jssdk需要用到的数据
            $data=array(
                'appId'=>$config['APPID'], //appid
                'timeStamp'=>strval($time), //时间戳
                'nonceStr'=>$unified_order['nonce_str'],// 随机字符串
                'package'=>'prepay_id='.$unified_order['prepay_id'],// 预支付交易会话标识
                'signType'=>'MD5'//加密方式
            );
            // 生成签名
            $data['paySign']=$this->makeSign($data);
            //dump( $unified_order);exit();
            return $data;
        }
    }

    /**
     * 生成支付二维码
     * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型：JSAPI，NATIVE，APP)
     */
    public function pay($order){
        $result=$this->unifiedOrder($order);
        $decodeurl=urldecode($result['code_url']);
        qrcode($decodeurl);
    }
    
    /**
     * 使用curl获取远程数据
     * @param  string $url url连接
     * @return string      获取到的数据
     */
    public function curl_get_contents($url){
        $ch=curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);                //设置访问的url地址
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验
        //curl_setopt($ch,CURLOPT_HEADER,1);                //是否显示头部信息
        curl_setopt($ch, CURLOPT_TIMEOUT, 500);               //设置超时
        curl_setopt($ch, CURLOPT_USERAGENT, _USERAGENT_);   //用户访问代理 User-Agent
        curl_setopt($ch, CURLOPT_REFERER,_REFERER_);        //设置 referer
        curl_setopt($ch,CURLOPT_FOLLOWLOCATION,1);          //跟踪301
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);        //返回结果
        $r=curl_exec($ch);
        curl_close($ch);
        return $r;
    }
    
    /**
     * 获取预支付订单
     * @param $body
     * @param $out_trade_no
     * @param $total_fee
     * @param null $notify_url
     * @return mixed
     */
    public function getPrePayOrder($body, $out_trade_no, $total_fee,$notify_url=null){
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        if(empty($notify_url)){
            $notify_url = $this->config["NOTIFY_URL"];
        }
        $onoce_str = $this->getRandChar(32);

        $data["appid"] = $this->config["APPID"];

        $data["body"] = $body;
        $data["mch_id"] = $this->config['MCHID'];
        $data["nonce_str"] = $onoce_str;
        $data["notify_url"] = $notify_url;
        $data["out_trade_no"] = $out_trade_no;
        $data["spbill_create_ip"] = $this->get_client_ip();
        //   $data["total_fee"] = $total_fee*100;
        $data["total_fee"] = $total_fee;
        $data["trade_type"] = "APP";
        $s = $this->makeSign($data);
        $data["sign"] = $s;
        // print_r($data);die;
        $xml = $this->arrayToXml($data);

        $response = $this->postXmlCurl($xml, $url);
        $obj=simplexml_load_string($response,'SimpleXMLElement',LIBXML_NOCDATA);

        $result = json_decode(json_encode($obj),TRUE);

        //将微信返回的结果xml转成数组
        return $result;
    }
    
    //数组转xml
    function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
            if (is_numeric($val))
            {
                $xml.="<".$key.">".$val."</".$key.">";

            }
            else
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
        }
        $xml.="</xml>";
        return $xml;
    }

    //post https请求，CURLOPT_POSTFIELDS xml格式
    public function postXmlCurl($xml,$url,$second=30)
    {
        //初始化curl
        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data)
        {
            curl_close($ch);
            return $data;
        }
        else
        {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error"."<br>";
            echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
            curl_close($ch);
            return false;
        }
    }
    
    //获取指定长度的随机字符串
    function getRandChar($length){
        $str = null;
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        $max = strlen($strPol)-1;

        for($i=0;$i<$length;$i++){
            $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
        }

        return $str;
    }
    
    //执行第二次签名，才能返回给客户端使用
    public function getOrder($prepayId){
        $data["appid"] = $this->config["APPID"];
        $data["noncestr"] = $this->getRandChar(32);
        $data["package"] = "Sign=WXPay";
        $data["partnerid"] = $this->config['MCHID'];
        $data["prepayid"] = $prepayId;
        $data["timestamp"] = time();
        $s = $this->makeSign($data, false);
        $data["sign"] = $s;
        
        return $data;
    }
    
    /**
     * 获取当前服务器IP
     * @return string
     */
    public  function get_client_ip()
    {
        if ($_SERVER['REMOTE_ADDR']) {
            $cip = $_SERVER['REMOTE_ADDR'];
        } elseif (getenv("REMOTE_ADDR")) {
            $cip = getenv("REMOTE_ADDR");
        } elseif (getenv("HTTP_CLIENT_IP")) {
            $cip = getenv("HTTP_CLIENT_IP");
        } else {
            $cip = "unknown";
        }
        return $cip;
    }
    
    /**
     * 1 成功  0  失败
     * 2016年8月31日下午2:31:04
     * @author 285018762@qq.com <飞鱼>
     * @param unknown $flag
     */
    public function returnXml($flag) {
        $data = array();
        if($flag){
            $data["return_code"] = "SUCCESS";
//             $data["appid"] =  $this->config["APPID"];//公众账号ID
//             $data["mch_id"] = $this->config['MCHID'];//商户号
//             $data["nonce_str"] = $this->getRandChar(32);//随机字符串
//             $data["sign"] = $this->makeSign($data);//签名
        }else{
            $data["return_code"] = "FAIL";
            $data["return_msg"]="签名失败";//返回信息
        }
        return  $this->arrayToXml($data);
    }
    
    /**
     * 极光推送
     * @param $client
     * @param $sence
     * @param $orderId
     * @return array|bool
     */
    public function Jpush($token,$msg,$type,$msg_data){
        //帮帮
        $ji=array(
            'appKey'=>'8614b01a9733364444c8c7d4',
            'masterSecret'=>'35a4e075f5d1ddcb4b3c25f3',
        );
        $client = new \JPush\Client($ji['appKey'],$ji['masterSecret']);
        $push = $client->push();
        $push->setPlatform('all');
        if($type==1){
            $push->addAlias($token);
        }else{
            $push->addAllAudience();
        }
        $push->iosNotification($msg, array(
                'alert'=>$msg,
                'badge' => '1',
                'extras' => $msg_data,
                'sound'=>'default'
            )
        );
        $push->androidNotification($msg, array(
            'badge' => '1',
            'extras' => $msg_data
        ));
        return $push->send();
    }
}